import java.util.Random;
import java.util.Scanner;

public class Tetris {
    private static final int WIDTH = 10;
    private static final int HEIGHT = 20;
    private static final char EMPTY = '.';
    private static final char BLOCK = '#';
    private static final char[][] grid = new char[HEIGHT][WIDTH];
    private static final Random random = new Random();
    private static final int[][][] SHAPES = {
            {{1, 1, 1, 1}}, // I
            {{1, 1}, {1, 1}}, // O
            {{1, 1, 1}, {0, 1, 0}}, // T
            {{1, 1, 0}, {0, 1, 1}}, // S
            {{0, 1, 1}, {1, 1, 0}}, // Z
            {{1, 1, 1}, {1, 0, 0}}, // L
            {{1, 1, 1}, {0, 0, 1}}  // J
    };

    private static int[][] currentShape;
    private static int currentX, currentY;

    public static void main(String[] args) {
        initGrid();
        spawnShape();

        Scanner scanner = new Scanner(System.in);
        while (true) {
            printGrid();
            System.out.println("Move (a: left, d: right, s: down, w: rotate, q: quit): ");
            char input = scanner.next().charAt(0);
            if (input == 'q') {
                break;
            }
            handleInput(input);
            if (!moveDown()) {
                mergeShape();
                clearLines();
                spawnShape();
                if (checkCollision()) {
                    System.out.println("Game Over!");
                    break;
                }
            }
        }
        scanner.close();
    }

    private static void initGrid() {
        for (int i = 0; i < HEIGHT; i++) {
            for (int j = 0; j < WIDTH; j++) {
                grid[i][j] = EMPTY;
            }
        }
    }

    private static void spawnShape() {
        currentShape = SHAPES[random.nextInt(SHAPES.length)];
        currentX = WIDTH / 2 - currentShape[0].length / 2;
        currentY = 0;
    }

    private static void printGrid() {
        for (int i = 0; i < HEIGHT; i++) {
            for (int j = 0; j < WIDTH; j++) {
                System.out.print(grid[i][j]);
            }
            System.out.println();
        }
    }

    private static void handleInput(char input) {
        switch (input) {
            case 'a':
                if (!checkCollision(currentX - 1, currentY)) {
                    currentX--;
                }
                break;
            case 'd':
                if (!checkCollision(currentX + 1, currentY)) {
                    currentX++;
                }
                break;
            case 's':
                moveDown();
                break;
            case 'w':
                rotateShape();
                break;
        }
    }

    private static boolean moveDown() {
        if (!checkCollision(currentX, currentY + 1)) {
            currentY++;
            return true;
        }
        return false;
    }

    private static void mergeShape() {
        for (int i = 0; i < currentShape.length; i++) {
            for (int j = 0; j < currentShape[i].length; j++) {
                if (currentShape[i][j] == 1) {
                    grid[currentY + i][currentX + j] = BLOCK;
                }
            }
        }
    }

    private static void clearLines() {
        for (int i = HEIGHT - 1; i >= 0; i--) {
            boolean full = true;
            for (int j = 0; j < WIDTH; j++) {
                if (grid[i][j] == EMPTY) {
                    full = false;
                    break;
                }
            }
            if (full) {
                for (int k = i; k > 0; k--) {
                    grid[k] = grid[k - 1].clone();
                }
                grid[0] = new char[WIDTH];
                for (int j = 0; j < WIDTH; j++) {
                    grid[0][j] = EMPTY;
                }
                i++;
            }
        }
    }

    private static boolean checkCollision() {
        return checkCollision(currentX, currentY);
    }

    private static boolean checkCollision(int x, int y) {
        for (int i = 0; i < currentShape.length; i++) {
            for (int j = 0; j < currentShape[i].length; j++) {
                if (currentShape[i][j] == 1) {
                    int newX = x + j;
                    int newY = y + i;
                    if (newX < 0 || newX >= WIDTH || newY >= HEIGHT || (newY >= 0 && grid[newY][newX] != EMPTY)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private static void rotateShape() {
        int[][] rotated = new int[currentShape[0].length][currentShape.length];
        for (int i = 0; i < currentShape.length; i++) {
            for (int j = 0; j < currentShape[i].length; j++) {
                rotated[j][currentShape.length - 1 - i] = currentShape[i][j];
            }
        }
        if (!checkCollision(currentX, currentY, rotated)) {
            currentShape = rotated;
        }
    }

    private static boolean checkCollision(int x, int y, int[][] shape) {
        for (int i = 0; i < shape.length; i++) {
            for (int j = 0; j < shape[i].length; j++) {
                if (shape[i][j] == 1) {
                    int newX = x + j;
                    int newY = y + i;
                    if (newX < 0 || newX >= WIDTH || newY >= HEIGHT || (newY >= 0 && grid[newY][newX] != EMPTY)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}